<!-- @Description:front/src/componentshome.vue @author:378237242@qq.com @date:2023/12/12 -->
<template>
  <n-space vertical>
    <n-space>
      <n-button type="primary" @click="onRecordAutioStart">
        {{ asrBtnText }}
        <n-countdown
          :duration="1000 * 60"
          :active="countdownActive"
          :render="renderCountdown"
          :on-finish="onCountdownFinish"
          ref="countdownRef"
        />
      </n-button>
      <n-button type="info" @click="onRecordAutioOff"> 结束语音输入 </n-button>
    </n-space>
    <n-input
      v-model:value="inputValue"
      clearable
      placeholder="语音输入的内容"
      type="textarea"
    />
    <!--<audio :src="audioData" controls></audio>-->
  </n-space>
</template>

<script lang="ts" setup>
import { computed, onUnmounted, ref, watchEffect } from "vue";
import { darkTheme, useDialog, createDiscreteApi } from "naive-ui";
import { CountdownProps } from "naive-ui";
import { NCountdown } from "naive-ui";
import RecorderManager from "@/assets/recordmanager/index.esm.js";
import axios from "axios";
import hmacSHA256 from "crypto-js/hmac-sha256";
import Base64 from "crypto-js/enc-base64";

const { message, dialog } = createDiscreteApi(["message", "dialog"]);
// const dialog = useDialog();
// let ASRSTATUS = "UNDEFINED"; // "UNDEFINED" "CONNECTING" "OPEN" "CLOSING" "CLOSED"

// 链接状态字典
enum ASRSTATUS {
  "CONNECTING" = "建立连接中",
  "OPEN" = "录音中",
  "CLOSING" = "关闭连接中",
  "CLOSED" = "开始语音输入",
}

// ASR 按钮链接状态类型
type Asrbtnstatus = keyof typeof ASRSTATUS;
// 当前按钮链接状态
const curAsrBtnStatus = ref<Asrbtnstatus>("CLOSED");
// 讯飞实例
const APPID = "554602d6";
const API_SECRET = "ZDI5MzMzM2I3NjkwMjJlZjU4MTQ0MDli";
const API_KEY = "9d005ff4e64b5af1c11d23b52e43c925";
// 按钮显示文字
// const asrBtnText = ref("开始语音输入");
const asrBtnText = computed(() => {
  return ASRSTATUS[curAsrBtnStatus.value];
});
// 转化后的文字
const inputValue = ref("");
// npl分析结果
const nplValue = ref("");
// 倒计时组件设置
const countdownActive = ref(false);
const countdownRef = ref<InstanceType<typeof NCountdown> | null>(null);
const renderCountdown: CountdownProps["render"] = ({ seconds }) => {
  return `${String(seconds).padStart(2, "6")}`;
};
const onCountdownFinish = () => {
  console.log("onCountdownFinish");
  // 倒计时结束，停止录音
  recorder.stop();
};
// const countdownDuration = ref(1000 * 60);
// watchEffect(() => {
//   asrBtnText.value = ASRSTATUS[curAsrBtnStatus.value];
// });
const recorder = new RecorderManager("./");

recorder.onStart = () => {
  changeASRSTATUS("OPEN");
};

let iatWS: WebSocket;
let resultText = "";
let resultTextTemp = "";
let countdownInterval;

/**
 * 获取websocket url
 * 该接口需要后端提供，这里为了方便前端处理
 */
function getWebSocketUrl() {
  // 请求地址根据语种不同变化
  let url = "wss://iat-api.xfyun.cn/v2/iat";
  let host = "iat-api.xfyun.cn";
  let apiKey = API_KEY;
  let apiSecret = API_SECRET;
  let date = new Date().toGMTString();
  let algorithm = "hmac-sha256";
  let headers = "host date request-line";
  let signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`;
  let signatureSha = hmacSHA256(signatureOrigin, apiSecret);
  let signature = Base64.stringify(signatureSha);
  let authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
  let authorization = btoa(authorizationOrigin);
  url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
  return url;
}

function toBase64(buffer: ArrayBuffer) {
  let binary = "";
  let bytes = new Uint8Array(buffer);
  // let len = bytes.byteLength;
  for (let i = 0, len = bytes.byteLength; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return window.btoa(binary);
}

function changeASRSTATUS(status: Asrbtnstatus) {
  curAsrBtnStatus.value = status;
  if (status === "CONNECTING") {
    inputValue.value = "";
    nplValue.value = "";
    resultTextTemp = "";
    resultText = "";
  } else if (status === "OPEN") {
    // 开始录音，启动倒计时
    countdownActive.value = true;
  } else if (status === "CLOSED") {
    console.log(inputValue.value);
    getNpls(inputValue.value);
  }
}

function renderResult(resultData: string) {
  // 识别结束
  let jsonData = JSON.parse(resultData);
  if (jsonData.data && jsonData.data.result) {
    let data = jsonData.data.result;
    let str = "";
    let ws = data.ws;
    for (let i = 0; i < ws.length; i++) {
      str = str + ws[i].cw[0].w;
    }
    // 开启wpgs会有此字段(前提：在控制台开通动态修正功能)
    // 取值为 "apd"时表示该片结果是追加到前面的最终结果；取值为"rpl" 时表示替换前面的部分结果，替换范围为rg字段
    if (data.pgs) {
      if (data.pgs === "apd") {
        // 将resultTextTemp同步给resultText
        resultText = resultTextTemp;
      }
      // 将结果存储在resultTextTemp中
      resultTextTemp = resultText + str;
    } else {
      resultText = resultText + str;
    }
    inputValue.value = resultTextTemp || resultText || "";
  }
  if (jsonData.code === 0 && jsonData.data.status === 2) {
    iatWS.close();
  }
  if (jsonData.code !== 0) {
    iatWS.close();
    console.error(jsonData);
  }
}

function connectWebSocket() {
  const websocketUrl = getWebSocketUrl();
  if ("WebSocket" in window) {
    iatWS = new WebSocket(websocketUrl);
  } else if ("MozWebSocket" in window) {
    /* eslint-disable-next-line no-undef */
    iatWS = new MozWebSocket(websocketUrl);
  } else {
    alert("浏览器不支持WebSocket");
    return;
  }
  changeASRSTATUS("CONNECTING");
  iatWS.onopen = (e) => {
    console.log("iatWS-onopen");
    // 开始录音
    recorder.start({
      sampleRate: 16000,
      frameSize: 1280,
    });
    const params = {
      common: {
        app_id: APPID,
      },
      business: {
        language: "zh_cn",
        domain: "iat",
        accent: "mandarin",
        vad_eos: 5000,
        // dwa: "wpgs",
      },
      data: {
        status: 0,
        format: "audio/L16;rate=16000",
        encoding: "raw",
      },
    };
    iatWS.send(JSON.stringify(params));
  };
  iatWS.onmessage = (e) => {
    renderResult(e.data);
  };
  iatWS.onerror = (e) => {
    console.error(e);
    recorder.stop();
    changeASRSTATUS("CLOSED");
  };
  iatWS.onclose = (e) => {
    console.log("iatWS-onclose");
    recorder.stop();
    changeASRSTATUS("CLOSED");
  };
}

recorder.onFrameRecorded = ({ isLastFrame, frameBuffer }) => {
  if (iatWS.readyState === iatWS.OPEN) {
    iatWS.send(
      JSON.stringify({
        data: {
          status: isLastFrame ? 2 : 1,
          format: "audio/L16;rate=16000",
          encoding: "raw",
          audio: toBase64(frameBuffer),
        },
      }),
    );
    if (isLastFrame) {
      changeASRSTATUS("CLOSING");
    }
  }
};
recorder.onStop = () => {
  countdownActive.value = false;
  countdownRef.value?.reset();
  iatWS.close();
};

const onRecordAutioStart = () => {
  if (curAsrBtnStatus.value === "CLOSED") {
    connectWebSocket();
  } else if (curAsrBtnStatus.value === "OPEN") {
    // 结束录音
    recorder.stop();
  }
};

const onRecordAutioOff = () => {
  recorder.stop();
};

const getNpls = (text: string) => {
  axios
    .get("http://localhost:3000/api/npl", { params: { text } })
    .then((res) => {
      console.log(res);
      if (res.data.code) {
        let speechInstance = new SpeechSynthesisUtterance(`抱歉，无法执行操作`);
        speechSynthesis.speak(speechInstance);
        dialog.warning({
          title: "警告",
          content: "抱歉，无法执行操作",
          positiveText: "确定",
        });
      } else {
        let speechInstance = new SpeechSynthesisUtterance(
          `${inputValue.value}指令操作成功`,
        );
        speechSynthesis.speak(speechInstance);
        dialog.success({
          title: "操作成功",
          content: `命中模型：
          ${JSON.stringify(res.data.data)}
          `,
          positiveText: "确定",
        });
      }
    })
    .catch((e) => {
      console.log(e);
    });
};
// getNpls("查询济南市污水管线");

let source: EventSource;

function connectEventSource() {
  if (!!window.EventSource) {
    source = new EventSource("http://localhost:3000/sse");
    source.onopen = function (event) {
      // handle open event
      console.log(source.readyState);
      console.log("长连接打开");
    };
    source.onmessage = function (event) {
      console.log(event);
      console.log(JSON.parse(event.data));
      console.log(source.readyState);
      console.log("收到长连接信息");
    };
    source.onerror = function (event) {
      console.log(source.readyState);
      console.log("长连接中断");
      // source.close();
    };
  }
}

// axios
//   .get("http://localhost:7001/api/npl", {
//     params: { text: "查询济南市地下管线数据" },
//   })
//   .then((res) => {
//     console.log(res);
//   });
// onUnmounted(() => {
//   iatWS.close();
//   source.close();
// });
</script>
